#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>

enum {
    KONG = 0,
    WALL = 1,
    FOOD = 2,
    HEAD = 3,
    BODY = 4
};    
#define ROW 29
#define COL 60
#define UP 72 
#define DOWN 80 
#define LEFT 75 
#define RIGHT 77 
#define SPACE 32 
#define ESC 27 
#define FONE 83 
#define FTWO 68 

struct SnakeHead {
	int len; 
	int x; 
	int y;
} snakehead;

struct Body {
	int x; 
	int y; 
} body[ROW * COL]; 

int zhuangtai[ROW][COL]; 
int max, grade;

int nandu[2] = { 1, 1 }, shijian = 3000, t; 

void game1();
void printSnake();
int welcometogame();
int aboutGame(void);
void HideCursor();
void CursorJump(int x, int y);
void InitInterface();
int color(int c);
void WriteGrade();
int writeFile(int grade);
void InitSnake();
void RandFood();
void JudgeFunc(int x, int y);
void DrawSnake(int flag);
void MoveSnake(int x, int y);
void run(int x, int y);
void Game();

int main() {
	int choice, a = 0;
	while (1) {
		choice = welcometogame();

		switch (choice) {
		case 1:
			system("cls");
			system("title 贪吃蛇"); 
			system("mode con cols=124 lines=30");
			game1();
			break;

		case 2:
			aboutGame();
			break;
			
		case 3:
			exit(0);
			break;

		default: {
			color(12);
			CursorJump(37, 30);
			printf("请在【1-3】内选择，按回车键重新选择");
		}
			   getchar();
		}
	}
	system("pause");
	return 0;
}
void game1() {
	HideCursor(); 
	WriteGrade(); 
	system("cls");
	InitInterface(); 
	InitSnake(); 
	srand((unsigned int)time(NULL)); 
	RandFood(); 
	DrawSnake(1); 
	Game(); 
}

void printSnake() {
	system("cls");

	CursorJump(35, 1);
	color(6);
	printf("/^\\/ ^\\");     

	CursorJump(34, 2);
	printf("|_θ|  θ|");      

	CursorJump(33, 2);
	color(2);
	printf("_");

	CursorJump(25, 3);
	color(12);
	printf("\\_/");        

	CursorJump(31, 3);
	color(2);
	printf("/");

	CursorJump(38, 3);
	color(6);
	printf("\\_/");       

	CursorJump(41, 3);
	color(10);
	printf("\\");

	CursorJump(26, 4);
	color(12);
	printf("\\_____"); 

	CursorJump(31, 4);
	color(2);
	printf("|");

	CursorJump(32, 4);
	printf("_________/");

	CursorJump(42, 4);
	color(10);
	printf("\\");

	CursorJump(31, 5);
	color(2);
	printf("\\________");     

	CursorJump(43, 5);
	color(10);
	printf("\\");

	CursorJump(38, 6);
	printf(" |    |                                    \\");

	CursorJump(38, 7);
	printf(" /    /                                    \\ \\");

	CursorJump(37, 8);
	printf(" /    /                                      \\  \\");

	CursorJump(36, 9);
	printf(" /    /                                        \\  \\");

	CursorJump(35, 10);
	printf(" /    /                                          \\  \\");

	CursorJump(34, 11);
	printf(" /    /                ╭-------╮                   \\  \\");

	CursorJump(33, 12);
	printf(" /    /             ╭---╯       ╰--╮                |   \\");

	CursorJump(33, 13);
	printf("(    (        ╭-----╯      ╭-╮     ╰--------╮     _/    |");

	CursorJump(34, 14);
	printf("\\    ╰----——-╯     ╭------╯ ╰-----╮        ╰----/      /");

	CursorJump(35, 15);
	printf("\\           ╭----—╯              ╰-—------╮      ╭-—-╯");

	CursorJump(36, 16);
	printf("╰—--------—╯                             ╰—----—╯        ");


}

int welcometogame() {
	int i = 0, j = 0;
	int choice = 0;
	printSnake();
	color(14);
	CursorJump(54, 18);
	printf("贪吃蛇游戏\n");

	color(11);
	for (i = 20; i <= 28; i++) {            
		for (j = 35; j <= 82; j++) {         
			CursorJump(j, i);
			if (i == 20 || i == 28) {
				printf("═");
			}
			else if (j == 35 || j == 82) {
				printf("║");
			}
			else {
				printf(" ");
			}
		}
	}
	
	color(7);
	CursorJump(39, 22);
	printf("1. gogogo~出发喽~！（先阅读游戏说明）");
	CursorJump(39, 24);
	printf("2. 榜一及游戏说明");
	CursorJump(39, 26);
	printf("3. 真的要退出游戏吗");
	CursorJump(37, 29);
	printf("请选择 [1 or 2 or 3]:");
	scanf("%d", &choice);
	getchar();
	return choice;
}

int aboutGame(void) {
	int i = 0;
	int j = 0;

	system("cls");

	color(11);
	CursorJump(54, 3);
	printf("游戏说明");

	color(14);
	
	for (i = 6; i <= 27; i++) {
		for (j = 30; j <= 85; j++) {
			CursorJump(j, i);
			
			if (i == 6 || i == 27) {
				printf("═");
			}
			else if (j == 30 || j == 85) {
				printf("║");
			}
		}
	}

	color(2);
	CursorJump(40, 8);
	printf("1.Do not撞到墙壁，do not咬到自己");

	color(10);
	CursorJump(40, 11);
	printf("2.F1冲冲冲 F2减减减");

	color(11);
	CursorJump(40, 14);
	printf("3.使用空格暂停游戏和继续游戏");

	color(3);
	CursorJump(40, 17);
	printf("4.使用方向键控制前进的方向");

	color(9);
	CursorJump(40, 20);
	printf("5.按下ESC键退出游戏");

	color(13);
	CursorJump(40, 23);
	printf("6.按下r或者R回到游戏欢迎界面");

	color(12);
	CursorJump(40, 26);
	printf("7.历史最高分：%d", max);


	color(15);
	CursorJump(30, 28);
	printf("按下回车键返回主界面...");

	getchar();

	return 0;
}

int writeFile(int grade) {
	FILE* fp = NULL;
	fp = fopen("贪吃蛇最高分.txt", "w");
	if (NULL == fp) {
		printf("打开文件失败..\n");
		return -1;
	}

	fwrite(&grade, sizeof(int), 1, fp);

	fclose(fp);
	fp = NULL;

	return grade;
}

void WriteGrade() {
	FILE* pf = fopen("贪吃蛇最高得分记录.txt", "w"); 
	if (pf == NULL) { 
		printf("保存最高得分记录失败\n");
		exit(0);
	}
	fwrite(&grade, sizeof(int), 1, pf); 
	fclose(pf); 
	pf = NULL; 
}


void HideCursor() {
	CONSOLE_CURSOR_INFO curInfo; 
	curInfo.dwSize = 1; 
	curInfo.bVisible = FALSE; 
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 
	SetConsoleCursorInfo(handle, &curInfo); 
}

void CursorJump(int x, int y) {
	COORD pos; 
	pos.X = x; 
	pos.Y = y; 
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(handle, pos); 
}

void InitInterface() {
	color(6); 
	for (int i = 0; i < ROW; i++) { 
		for (int j = 0; j < COL; j++) {
			if (j == 0 || j == COL - 1) {
				zhuangtai[i][j] = WALL; 
				CursorJump(2 * j, i);
				printf("■");
			}
			else if (i == 0 || i == ROW - 1) { 
				zhuangtai[i][j] = WALL; 
				CursorJump(2 * j, i);
				printf("■");
			}
			else {
				zhuangtai[i][j] = KONG; 

			}
		}
	}
	color(7); 
	CursorJump(0, ROW);
	printf("当前得分:%d", grade);
	color(12);
	CursorJump(COL, ROW);
	printf("历史最高得分:%d", max);
}

int color(int c) {
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
	return 0;
}

void InitSnake() {

	snakehead.len = 4;
	snakehead.x = COL / 2; 
	snakehead.y = ROW / 2; 
	
	for (int i = 0; i <= 3; i++) {
		body[i].x = COL / 2 - 1 - i;
		body[i].y = ROW / 2;

	}
	
	zhuangtai[snakehead.y][snakehead.x] = HEAD;
	zhuangtai[body[0].y][body[0].x] = BODY;
	zhuangtai[body[1].y][body[1].x] = BODY;

}


void RandFood() {
	int i, j;
	do {
	
		i = rand() % ROW;
		j = rand() % COL;
	} while (zhuangtai[i][j] != KONG); 
	zhuangtai[i][j] = FOOD; 
	color(12); 
	CursorJump(2 * j, i); 
	printf("◆");
}


void JudgeFunc(int x, int y) {

	if (zhuangtai[snakehead.y + y][snakehead.x + x] == FOOD) {
		shijian -= 100;
		snakehead.len++; 
		grade += nandu[1]; 
		color(7); 
		CursorJump(0, ROW);
		printf("当前得分:%d", grade); 
		RandFood(); 
	}

	else if (zhuangtai[snakehead.y + y][snakehead.x + x] == WALL || zhuangtai[snakehead.y + y][snakehead.x + x] == BODY) {
		Sleep(1000);
		system("cls"); 
		color(14); 
		CursorJump(2 * (COL / 3), ROW / 2 - 3);
		if (grade > max) {
			printf("恭喜你打破最高记录，最高记录更新为%d", grade);
			max = writeFile(grade);
		}
		else if (grade == max) {
			printf("与最高记录持平，加油!再创佳绩", grade);
		}
		else {
			printf("请继续加油，当前与最高记录相差%d", max - grade);
		}
		color(12);
		CursorJump(2 * (COL / 3), ROW / 2);
		printf("GAME OVER······");
		grade = 0;
		while (1) { 
			char ch;
			color(15);
			CursorJump(2 * (COL / 3), ROW / 2 + 3);
			printf("(回到主界面请按下r)");
			CursorJump(2 * (COL / 3), ROW / 2 + 5);
			printf("再来一把?(y/n):");
			scanf("%c", &ch);
			if (ch == 'y' || ch == 'Y') {
				system("cls");
				getchar();
				game1();
			}
			else if (ch == 'r' || ch == 'R') {
				system("cls");
				getchar();
				main();
			}
			else if (ch == 'n' || ch == 'N') {
				CursorJump(2 * (COL / 3), ROW / 2 + 7);
				exit(0);
			}
			else {
				CursorJump(2 * (COL / 3), ROW / 2 + 5);
				printf("选择错误，请再次选择");
			}
		}
	}
}

void run(int x, int y) {
	int t = 0;
	while (1) {
		if (t == 0 && nandu[1] == 1)
			t = shijian;
		else if (nandu[1] > nandu[0])
			t = shijian - (nandu[1] - nandu[0]) * 100;
		if (nandu[1] < nandu[0])
			t = shijian + (nandu[0] - nandu[1]) * 100;

		while (--t) {
			if (_kbhit() != 0) 
				break;
		}
		if (t == 0) { 
			JudgeFunc(x, y); 
			MoveSnake(x, y); 
		}
		else { 
			break; 
		}
	}
}

void Game() {
	int n = RIGHT; 
	int tmp = 0; 
	goto first; 
	while (1) {
		n = _getch(); 
		switch (n) {
		case UP:
		case DOWN:
			if (tmp != LEFT && tmp != RIGHT) { 
				n = tmp;
			}
			break;
		case LEFT:
		case RIGHT: 
			if (tmp != UP && tmp != DOWN) { 
				n = tmp; 
			}

		case SPACE:
		case ESC:
		case 'r':
		case 'R':
			break; 
		default:
			n = tmp; 
			break;
		}
	first: 
		switch (n) {
		case UP: 
			run(0, -1);
			tmp = UP; 
			break;
		case DOWN:
			run(0, 1); 
			tmp = DOWN; 
			break;
		case LEFT:
			run(-1, 0); 
			tmp = LEFT; 
			break;
		case RIGHT:
			run(1, 0); 
			tmp = RIGHT; 
			break;
		case FONE:
			t = shijian - (nandu[1] - nandu[0]) * 100;
			break;
		case FTWO:
			t = shijian + (nandu[0] - nandu[1]) * 100;
			break;
		case SPACE: 
			system("pause>nul");
			break;
		case ESC: 
			system("cls");
			color(7); 
			CursorJump(COL - 8, ROW / 2);
			printf("  游戏结束  ");
			CursorJump(COL - 8, ROW / 2 + 2);
			exit(0);
		case 'r':
		case 'R': 
			system("cls"); 
			main(); 
		}
	}
}


void DrawSnake(int flag) {
	if (flag == 1) {
		color(14); 
		CursorJump(2 * snakehead.x, snakehead.y);
		printf("●"); 
		for (int i = 0; i < snakehead.len; i++) {
			CursorJump(2 * body[i].x, body[i].y);
			printf("●"); 
		}
	}
	else { 
		if (body[snakehead.len - 1].x != 0) {
			CursorJump(2 * body[snakehead.len - 1].x, body[snakehead.len - 1].y);
			printf("  ");
		}
	}
}


void MoveSnake(int x, int y) {
	DrawSnake(0); 
	zhuangtai[body[snakehead.len - 1].y][body[snakehead.len - 1].x] = KONG; 
	zhuangtai[snakehead.y][snakehead.x] = BODY; 
	
	for (int i = snakehead.len - 1; i > 0; i--) {
		body[i].x = body[i - 1].x;
		body[i].y = body[i - 1].y;
	}

	body[0].x = snakehead.x;
	body[0].y = snakehead.y;

	snakehead.x = snakehead.x + x;
	snakehead.y = snakehead.y + y;
	DrawSnake(1); 
}
